/*
 * $Id: Daemon.h,v 1.6 2007-06-20 11:29:41 baconevi Exp $
 */

#ifndef _XPP_SYS_DAEMON_CLASS_
#define _XPP_SYS_DAEMON_CLASS_

#include <xpp/Named.h>
#include <xpp/bas/SignalAgent.h>
#include <xp/bas/sysapi.h>

namespace xpp
{
	namespace sys
	{

		#ifdef _WIN32
		class DaemonWaitHint 
		{
		public:
			DaemonWaitHint (
				DWORD start_hint  = 30000, 
				DWORD stop_hint   = 30000, 
				DWORD pause_hint  = 30000, 
				DWORD resume_hint = 30000) 
			{
				dwh_start  = start_hint;
				dwh_stop   = stop_hint;
				dwh_pause  = pause_hint;
				dwh_resume = resume_hint;
			}
		
			DWORD startHint () const
			{
				return dwh_start;
			}
			DWORD stopHint () const
			{
				return dwh_stop;
			}
			DWORD pauseHint () const
			{
				return dwh_pause;
			}
			DWORD resumeHint () const
			{
				return dwh_resume;
			}
		
			void setStartWaitHint (DWORD hint) 
			{
				dwh_start = hint;
			}
			void setStopWaitHint (DWORD hint) 
			{
				dwh_stop = hint;
			}
			void setPauseWaitHint (DWORD hint) 
			{
				dwh_pause = hint;
			}
			void setResumeWaitHint (DWORD hint) 
			{
				dwh_resume = hint;
			}
		
		protected:
			DWORD dwh_start;
			DWORD dwh_stop;
			DWORD dwh_pause;
			DWORD dwh_resume;
		};

		class DaemonProperty 
		{
		public:
			DaemonProperty (
				const TCHAR* name = XP_T(""),
				// SERVICE_WIN32_OWN_PROCESS
				// SERVICE_WIN32_SHARE_PROCESS
				// SERVICE_KERNEL_DRIVER
				// SERVICE_FILE_SYSTEM_DRIVER
				// SERVICE_INTERACTIVE_PROCESS
				DWORD service_type = SERVICE_WIN32_OWN_PROCESS,
				// SERVICE_BOOT_START, SERVICE_SYSTEM_START
				// SERVICE_AUTO_START, SERVICE_DEMAND_START
				// SERVICE_DISABLED
				DWORD start_type = SERVICE_AUTO_START,
				// SERVICE_ERROR_IGNORE, SERVICE_ERROR_NORMAL
				// SERVICE_ERROR_SEVERE, SERVICE_ERROR_CRITICAL
				DWORD error_control = SERVICE_ERROR_NORMAL): 
				service_type(service_type), 
				start_type(start_type), 
				error_control(error_control)
			{
				xp_strxcpy (display_name, xp_countof(display_name), name);
			}
		
			const TCHAR* displayName () const
			{
				return display_name;
			}
			DWORD serviceType () const
			{
				return service_type;
			}
			DWORD startType () const
			{
				return start_type;
			}
			DWORD errorControl () const
			{
				return error_control;
			}
		
			void setDisplayName (const TCHAR* name) 
			{
				xp_strxcpy (display_name, xp_countof(display_name), name);
			}
			void setServiceType (DWORD type) 
			{
				service_type = type;
			}
			void setStartType (DWORD type) 
			{
				start_type = type;
			}
			void setErrorControl (DWORD control) 
			{
				error_control = control;
			}
		
			enum 
			{
				MAX_DISPLAY_NAME_LEN = 128
			};

		protected:
			TCHAR display_name[MAX_DISPLAY_NAME_LEN + 1];
			DWORD service_type;
			DWORD start_type;
			DWORD error_control;
		};
		#endif

		class Daemon: public Named, public xpp::bas::SignalAgent
		{
		public:
			Daemon (bool root_only = false);
			Daemon (const xp_char_t* name, bool root_only = false);
			virtual ~Daemon ();
		
			enum 
			{
				ERR_NONE     = 0,
				ERR_NON_ROOT = 1,
				ERR_CHDIR    = 2,
				ERR_FORK     = 3,
				ERR_SETSID   = 4,
				ERR_SCD      = 5
			};
			int start (int* err_code);
			int startAsNonDaemon (int* err_code);
		
			int returnCode () const
			{
				return return_code;
			}

			void setReturnCode (int ret)
			{
				return_code = ret;
			}

			bool isRootOnly () const
			{
				return root_only;
			}
			
			virtual int run      () = 0;
			virtual int stop     () = 0;
			virtual int shutdown () = 0;
			virtual int pause    () = 0;
			virtual int resume   () = 0;
		
		#ifdef _WIN32
			const TCHAR* path () const
			{
				DWORD ret;
				ret = ::GetModuleFileName (
					NULL, (LPTSTR)daemon_path, 
					xp_countof (daemon_path) - 1);
				if (ret == 0) daemon_path[0] = XP_T('\0');
				return daemon_path;
			}

			DaemonWaitHint& waitHint ()
			{
				return wait_hint;
			} 
			const DaemonWaitHint& waitHint () const
			{
				return wait_hint;
			} 

			DaemonProperty& property ()
			{
				return daemon_property;
			}
			const DaemonProperty& property () const
			{
				return daemon_property;
			}
		#endif

		protected:
			bool   root_only;
			int    return_code;

		#ifdef _WIN32
			mutable TCHAR daemon_path[MAX_PATH + 1]; 	
			DaemonWaitHint  wait_hint;
			DaemonProperty  daemon_property;

			SERVICE_STATUS_HANDLE status_handle;
			SERVICE_STATUS        daemon_status;

			BOOL report_action_complete (DWORD state, DWORD exit_code = 0);
			BOOL report_action_start    (DWORD state, DWORD hint);
			BOOL report_action_progress ();

			static void WINAPI service_main (DWORD argc, LPTSTR* argv);
			static void WINAPI control_handler (DWORD control_code);
		#endif
		};
		
		class UnpausableDaemon: public Daemon 
		{
		public:
			UnpausableDaemon (bool root_only = false);
			UnpausableDaemon (const xp_char_t* name, bool root_only = false);

			int pause  ()
			{ 
				return 0;
			}
			int resume () 
			{
				return 0;
			}
		};
		
	}
}
		
#endif
		
